﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stddef.h>
//浮点数在内存中是有可能不能精确保存的 switch的比较 浮点数的比较
//struct Stu
//{
//	char name[20];//名字
//	int age;//年龄
//	char sex[5];//性别
//	char id[20];//学号
//};
//int main(void)
//{
//	//按照指定的顺序初始化
//	struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex ="⼥" };
//	printf("name: %s\n", s2.name);
//	printf("age : %d\n", s2.age);
//	printf("sex : %s\n", s2.sex);
//	printf("id : %s\n", s2.id);
//
//	return 0;
//}
//struct
//{
//	int a;
//	char b;
//	float c;
//}x;//匿名结构体声明，只可以在此创建。
//struct
//{
//	int a;
//	char b;
//	float c;
//}a[20], * p;
//int main(void)
//{
//	p = &x;
//	/*
//		编译器会把上⾯的两个声明当成完全不同的两个类型，所以是⾮法的。
//		匿名的结构体类型，如果没有对结构体类型重命名的话，基本上只能使⽤⼀次
//	*/
//	return 0;
//}
//struct Node
//{
//	int data;
//	struct Node next;
//};//无穷大
//struct Node
//{
//	int data;
//	struct Node* next;
//};
//typedef struct
//{
//	int data;
//	Node* next;
//}Node;
////因为Node是对前⾯的匿名结构体类型的重命名产⽣的，但是在匿名结构体内部提前使⽤Node类型来创建成员变量，这是不⾏的。
//typedef struct Node
//{
//	int data;
//	struct Node* next;//此处名字来源第一行，未进行重命名之前
//}Node;
/*
	结构体的对⻬规则：
	1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
	2. 其他成员变量要对⻬到某个数字（对⻬数）的整数倍的地址处。
	对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
	- VS 中默认的值为 8
	- Linux中 gcc 没有默认对⻬数，对⻬数就是成员⾃⾝的⼤⼩
	3. 结构体总⼤⼩为最⼤对⻬数（结构体中每个成员变量都有⼀个对⻬数，所有对⻬数中最⼤的）的整数倍。
	4. 如果嵌套了结构体的情况，嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处，结构
	体的整体⼤⼩就是所有最⼤对⻬数（含嵌套结构体中成员的对⻬数）的整数倍。

*/
//struct S1
//{
//	char c1;
//	int i;
//	char c2;
//};
//struct S2
//{
//	char c1;
//	char c2;
//	int i;
//};
//struct S3
//{
//	double d;
//	char c;
//	int i;
//};
//struct S4
//{
//	char c1;
//	struct S3 s3;
//	double d;
//};
//int main(void)
//{
//	/*
//		那在设计结构体的时候，我们既要满⾜对⻬，⼜要节省空间，如何做到：
//		让占⽤空间⼩的成员尽量集中在⼀起
//	*/
//	size_t a = offsetof(struct S1, c1);
//	printf("%zd\n", sizeof(struct S1));
//	printf("%zd\n", sizeof(struct S2));
//	printf("%zd\n", sizeof(struct S3));
//	printf("%zd\n", sizeof(struct S4));
//	return 0;
//}
//#pragma pack(1)//设置默认对⻬数为1
//struct S
//{
//	char c1;
//	int i;
//	char c2;
//};
//#pragma pack()//取消设置的对⻬数，还原为默认
//int main()
//{
//	//输出的结果是什么？
//	printf("%zd\n", sizeof(struct S));
//	return 0;
//}
//struct S
//{
//	int data[1000];
//	int num;
//};
//struct S s = { {1,2,3,4}, 1000 };
////结构体传参
//void print1(struct S s)
//{
//	printf("%d\n", s.num);
//}
////结构体地址传参
//void print2(struct S* ps)
//{
//	printf("%d\n", ps->num);
//}
//int main()
//{
//	print1(s); //传结构体
//	print2(&s); //传地址
//	return 0;
//}
/*
	函数传参的时候，参数是需要压栈，会有时间和空间上的系统开销。
	如果传递⼀个结构体对象的时候，结构体过⼤，参数压栈的的系统开销⽐较⼤，所以会导致性能的下降。
	结论：结构体传参的时候，要传结构体的地址。
*/
/*
	位段的声明和结构是类似的，有两个不同：
	1. 位段的成员必须是 int、unsigned int 或signed int ，在C99中位段成员的类型也可以选择其他类型。
	2. 位段的成员名后边有⼀个冒号和⼀个数字。
*/
//struct A
//{
//	int _a : 2;//位段中的位是bit位
//	int _b : 5;
//	int _c : 10;
//	int _d : 30;
//};
//int main(void)
//{
//	printf("%zd\n", sizeof(struct A));
//	return 0;
//}
/*
	 位段的内存分配
	1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
	2. 位段的空间上是按照需要以4个字节（ int ）或者1个字节（ char ）的⽅式来开辟的。
	3. 位段涉及很多不确定因素，位段是不跨平台的，注重可移植的程序应该避免使⽤位段。
*/
/*
	位段的跨平台问题
	1. int 位段被当成有符号数还是⽆符号数是不确定的。
	2. 位段中最⼤位的数⽬不能确定。（16位机器最⼤16，32位机器最⼤32，写成27，在16位机器会出问题。
	3. 位段中的成员在内存中从左向右分配，还是从右向左分配，标准尚未定义。
	4. 当⼀个结构包含两个位段，第⼆个位段成员⽐较⼤，⽆法容纳于第⼀个位段剩余的位时，是舍弃剩余的位还是利⽤，这是不确定的。
*/
/*
	位段的⼏个成员共有同⼀个字节，这样有些成员的起始位置并不是某个字节的起始位置，那么这些位置处是没有地址的。内存中每个字节分配⼀个地址，⼀个字节内部的bit位是没有地址的。
	所以不能对位段的成员使⽤&操作符，这样就不能使⽤scanf直接给位段的成员输⼊值，只能是先输⼊放在⼀个变量中，然后赋值给位段的成员。
*/
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main()
{
	struct A sa = { 0 };
	//scanf("%d", &sa._b);//这是错误的

	//正确的⽰范
	int b = 0;
	scanf("%d", &b);
	sa._b = b;
	return 0;
}